package pe.com.nextel.rms;

import pe.com.nextel.http.MensajeHttp;
import java.io.IOException;
import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordComparator;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordFilter;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;
import javax.microedition.rms.RecordStoreNotOpenException;
import pe.com.nextel.util.ArrayList;
import pe.com.nextel.util.Iterator;
import pe.com.nextel.util.List;

/**
 *
 * Es la clase base de administrar el acceso el los recordStore RMS
 * @author eespinoza
 * @version 1.2, 30/11/2007
 * @since 1.1
 */
public abstract class GestorRMS {
    
    public static short ORD_ASC = 0;
    public static short ORD_DESC = 1;
    // Name of the store
    private String storeName;
    // Number of times the store has been opened.
    private int storeOpened = 0;
    // Instance of the persistent store
    private RecordStore rs;
    // para sincronizar el acceso al RMS
    private boolean available = false;
    // si el rms debe ser sincronizado
    protected boolean isSicronizado = false;
    
    /**
     * Abstract method to create an empty record.
     * <P>
     * <b>Example: </b>
     * <pre>
     * <code>
     * public class MyStore extends GestorRMS
     * {
     *   ...
     *   protected OAbstractRecord createRecord()
     *   {
     *     return new MyRecord();
     *   }
     * </code>
     * </pre>
     * where MyRecord is the name of your record.
     * @return An empty OAbstractRecord.
     */
    abstract protected Persistente createObjectBean();
    
    /**
     * Es el metodo que se debe redefinir para guardar los datos de un objeto en el
     * Record Store
     * @param object el objeto que encapsula los datos a guardar
     * @throws Exception excepcion generada en el proceso de guardado
     */
    protected synchronized int guardar(Persistente object) throws IOException, RecordStoreFullException, RecordStoreException {
        int i = 0;
        byte[] arrByte = object.persistir(null);
        if (object.getIdRecodset() == 0) {
            i = rs.addRecord(arrByte, 0, arrByte.length);
            object.setIdRecodset(i);
        } else {
            rs.setRecord(object.getIdRecodset(), arrByte, 0, arrByte.length);
            i = object.getIdRecodset();
        }
        return i;
    }
    
    /**
     *
     * @param arrByte
     * @throws java.io.IOException
     * @throws javax.microedition.rms.RecordStoreFullException
     * @throws javax.microedition.rms.RecordStoreException
     */
    protected synchronized int guardar(byte[] arrByte) throws IOException, RecordStoreFullException, RecordStoreException {
        return rs.addRecord(arrByte, 0, arrByte.length);
        
    }
    
    /**
     * Es el metodo que se debe redefinir para guardar los datos de un objeto en el
     * Record Store
     * @param object el objeto que encapsula los datos a guardar
     * @throws Exception excepcion generada en el proceso de guardado
     */
    protected synchronized int guardar(List lista) throws IOException, RecordStoreFullException, RecordStoreException {
        byte[] arrByte = createObjectBean().persistir(lista);
        return rs.addRecord(arrByte, 0, arrByte.length);
    }
    
    /**
     * Actualiza una lista por el recorID
     */
    protected synchronized void actualizarLista(int i, List lista) throws IOException, RecordStoreFullException, RecordStoreException {
        byte[] arrByte = createObjectBean().persistir(lista);
        rs.setRecord(i, arrByte, 0, arrByte.length);
    }
    
    /**
     * Obtiene el nombre del Record Store
     * @return el nombre
     * @throws Exception excepcion generada en el proceso.
     */
    public String getNombre() throws RecordStoreNotOpenException {
        return rs.getName();
    }
    
    /**
     * ingresa el nombre del RecorStore
     */
    protected GestorRMS(String name) {
        storeName = name;
    }
    
    /**
     *     /**
     * Deletes a record in the persistent store.
     * @throws javax.microedition.rms.RecordStoreException
     */
    public synchronized void deleteAll() throws RecordStoreException {
        try {
            
            abrirRS();
            if (getNumRecords() > 0) {
                cerrarRS();
                
                RecordStore.deleteRecordStore(storeName);
            } else {
                
                cerrarRS();
            }
        } catch (RecordStoreNotFoundException e) {
            e.printStackTrace();
            // Data is already deleted, don't bother throwing exception
        }
    }
    
    /**
     * @deprecate
     * elimina por recordID
     * @param id
     * @throws javax.microedition.rms.RecordStoreNotOpenException
     * @throws javax.microedition.rms.InvalidRecordIDException
     * @throws javax.microedition.rms.RecordStoreException
     */
    public synchronized void deleteRecord(int id) throws RecordStoreNotOpenException, InvalidRecordIDException, RecordStoreException {
        rs.deleteRecord(id);
    } // deleteRecord
    
    /**
     *  abre el recordID
     * @throws javax.microedition.rms.RecordStoreException
     */
    public synchronized void abrirRS() throws RecordStoreException {
        // verificamos si el RMS debe ser sincronizado
        if (isSicronizado) {
            while (available == true) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
            
            available = true;
            notify();
            
            rs = RecordStore.openRecordStore(storeName, true);
            storeOpened++;
            
        } else {
            // System.out.println("abrir openRecordStore=" + storeName);
            rs = RecordStore.openRecordStore(storeName, true);
            // System.out.println("abierto "+ storeName);
            storeOpened++;
        }
    }//abrirRS
    
    /**
     * Cierra el recordStore
     * @throws javax.microedition.rms.RecordStoreNotOpenException
     * @throws javax.microedition.rms.RecordStoreException
     */
    public synchronized void cerrarRS() throws RecordStoreNotOpenException, RecordStoreException {
        if (isSicronizado) {//verificamos si es sincronizado el RecorsTore
            while (available == false) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
            available = false;
            notify();
            
            if (storeOpened > 0) {
                rs.closeRecordStore();
                storeOpened--;
            }
            
        } else {
            
            if (storeOpened > 0) {
                //System.out.println("cerrando=" + storeName);
                rs.closeRecordStore();
                //System.out.println("cerrado");
                storeOpened--;
            }
        }
    }//cerrarRS
    
    /**
     * Obtiene
     * @param id el recordID del registro.
     * @return los registros o el registro en byte[]
     * @throws javax.microedition.rms.RecordStoreNotOpenException
     * @throws javax.microedition.rms.InvalidRecordIDException
     * @throws javax.microedition.rms.RecordStoreException
     */
    protected byte[] getRecord(int id) throws RecordStoreNotOpenException, InvalidRecordIDException, RecordStoreException {
        return rs.getRecord(id);
    }
    
    /**
     * Obtiene el objeto
     * @param id
     * @return
     * @throws javax.microedition.rms.RecordStoreNotOpenException
     * @throws javax.microedition.rms.InvalidRecordIDException
     * @throws javax.microedition.rms.RecordStoreException
     * @throws java.io.IOException
     */
    protected Persistente getRecordObject(int id) throws RecordStoreNotOpenException, InvalidRecordIDException, RecordStoreException, IOException {
        Persistente bean = createObjectBean();
        byte[] arrByte = getRecord(id);
        bean.readObject(arrByte);
        bean.setIdRecodset(id);
        return bean;
    }
    
    public Persistente getObject(int idRecordSet) throws Exception {
        try {
            abrirRS();
            return getRecordObject(idRecordSet);
        } finally {
            cerrarRS();
        }
    }
 
    /**
     * retorna en una lista
     */
    protected List getRecordList(int id) throws RecordStoreNotOpenException, InvalidRecordIDException, RecordStoreException, IOException {
        
        Persistente bean = createObjectBean();
        
        byte[] arrByte = getRecord(id);
         
        List lista = bean.readObject(arrByte);
         
        return lista;
    }
    
    /**
     * Devuelve el numero de registros almacenados en el Record Store.
     * @return numero de registros
     * @throws Exception excepcion generada en el proceso.
     */
    protected synchronized int getNumRecords() throws RecordStoreNotOpenException {
        return rs.getNumRecords();
    }
    
    /**
     *
     * @return A RecordEnumeration containing the byte[] records that match
     * your search criteria.
     * @throws javax.microedition.rms.RecordStoreNotOpenException
     */
    protected synchronized RecordEnumeration enumerateRecords() throws RecordStoreNotOpenException {
        return rs.enumerateRecords(null, null, false);
    }
    
    /**
     * Gets an enumeration of records from the RecordStore.
     * THIS METHOD DOES NOT USE DATA CACHING. It should only be used
     * when you want to utilize the keepUpdated flag (e.g. Always have
     * an updated RecordEnumeration ).
     * @param filter Used to filter records. May be null.
     * @param comparator Used to sort records. May be null.
     * @param keepUpdated When true, will keep the RecordEnumeration that is
     * passed back an accurate reflection of what is in the data store.
     * @returns A RecordEnumeration containing the byte[] records that match
     * your search criteria.
     * @throws RecordStoreNotOpenException
     */
    protected synchronized RecordEnumeration enumerateRecords(RecordFilter filter, RecordComparator comparator, boolean keepUpdated)
    throws RecordStoreNotOpenException {
        return rs.enumerateRecords(filter, comparator, keepUpdated);
    }
    
    /**
     * Obtiene una lista de objetos cuando este es guardado uno a uno
     * @return
     * @throws java.lang.Exception
     */
    public List getAllBean() throws Exception {
        RecordEnumeration en = null;
        try {
            abrirRS();
            
            List lista = new ArrayList();
            en = enumerateRecords();
            
            while (en.hasNextElement()) {
                int id = en.nextRecordId();
                Persistente bean = getRecordObject(id);
                
                lista.add(bean);
            }
            return lista;
        } finally {
            
            if (en != null) {
                en.destroy();
                en = null;
            }
            
            cerrarRS();
        }
    }
    
    public void getAllBytes(MensajeHttp mensaje) throws Exception {
        RecordEnumeration en = null;
        try {
            mensaje.iniciarRead();
            
            abrirRS();
            
            en = enumerateRecords();
            
            mensaje.getDataOuput().writeInt(getNumRecords());
            
            while (en.hasNextElement()) {
                int id = en.nextRecordId();
                
                mensaje.getDataOuput().write(getRecord(id));
                
            }
            
            
            
        } finally {
            
            
            
            if (en != null) {
                en.destroy();
                en = null;
            }
            
            cerrarRS();
            
            mensaje.cerrarRead();
        }
    }
    
    /**
     * Obtinene una lista de objetos cuando este es guardado en grupo
     * @return
     * @throws java.lang.Exception
     */
    public List getAllLista() throws Exception {
        RecordEnumeration en = null;
        try {
            abrirRS();
            
            List lista = new ArrayList();
            
            en = enumerateRecords();
            Persistente bean;
            while (en.hasNextElement()) {
                int id = en.nextRecordId();
                Iterator it = getRecordList(id).iterator();
                while (it.hasNext()) {
                    
                    bean = (Persistente) it.next();
                    bean.setIdRecodset(id);
                    lista.add(bean);
                }
            }
            return lista;
            
        } finally {
            if (en != null) {
                en.destroy();
                en = null;
            }
            cerrarRS();
        }
        
    }
    
    /**
     * Nuevo
     * Solo para aquello objetos que maneja IdRecordSet y Pagina
     *
     */
    public Persistente getObject(int idRecordSet, int idPosicion) throws Exception {
        try {
            abrirRS();
            if(idRecordSet > 0 ){
                byte[] array = getRecord(idRecordSet);
                Persistente persistente = (Persistente) createObjectBean().readObject(array).get(idPosicion - 1);
                persistente.setIdRecodset(idRecordSet);
                return persistente;
            }else{
                return null;
            }
            
        } finally {
            cerrarRS();
        }
    }
    
    /* elimina por recordID
     * Nuevo
     * @param id
     * @throws javax.microedition.rms.RecordStoreNotOpenException
     * @throws javax.microedition.rms.InvalidRecordIDException
     * @throws javax.microedition.rms.RecordStoreException
     **/
    public void deleteObjectIDRecordSet(int recordId) throws Exception {
        try {
            abrirRS();
            rs.deleteRecord(recordId);
        } finally {
            cerrarRS();
        }
    }
    
    /**
     * Nuevo
     *
     **/
    public void actualizarObjectLista(PersistenteX persistente) throws Exception {
        try {
            abrirRS();
           //System.out.println("Persistente= " + persistente);
            byte[] array = getRecord(persistente.getIdRecodset());
//            System.out.println("Paso 1---->");
            List lista = persistente.readObject(array);
            lista.set(persistente.getPosicionID() - 1, persistente);
            actualizarLista(persistente.getIdRecodset(), lista);
            
            lista.clear();
            lista = null;
            
        } finally {
            cerrarRS();
        }
    }
    

    
    /**
     * Nuevo
     * Obtiene cuantos objectos tiene el recordset siempre y cuando sus elementos sean del mismo tamanio
     **/
    public int cantidadObjectLista(int tamanioGrupo) throws Exception {
        
//         System.out.println("cantidadObjectLista=" + storeName+ "|" );
        
        List lista = null;
        try {
            
            abrirRS();
            
//              System.out.println("getNumRecords( )=" + getNumRecords( ));
            // verificamos que tenga datos
            if (getNumRecords() <= 0) {
                return 0;
            }
            
            
            byte[] array = getRecord(getNumRecords());
            
//            System.out.println("1");
            
            lista = createObjectBean().readObject(array);
            
//             System.out.println("lista=" + lista.size());
            
            return (lista != null) ? (getNumRecords() - 1) * tamanioGrupo + lista.size() : 0;
            
        } finally {
            
            if (lista != null) {
                lista.clear();
                lista = null;
            }
            
            cerrarRS();
        }
    }
    
    public int cantidadObject() throws Exception {
        try {
            abrirRS();
            return rs.getNumRecords();
        } finally {
            cerrarRS();
        }
    }
}
